package com.loser.gateway.utils;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.google.common.collect.Maps;
import com.loser.gateway.base.CmdMeta;
import com.loser.gateway.mysql.entity.CmdConfig;
import com.loser.gateway.mysql.service.CmdConfigService;
import com.loser.module.base.proto.base.LiveBaseReq;
import com.loser.utils.BusinessException;
import com.loser.utils.ContextUtil;
import com.loser.utils.ParamsUtil;
import com.loser.utils.SerializationUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import lombok.Data;

import java.time.Duration;
import java.util.Map;
import java.util.Objects;

public class CmdUtils {

    private static final Cache<Integer, CmdMeta> CMD_META_MAP = Caffeine.newBuilder().initialCapacity(10)
            .maximumSize(500).expireAfterWrite(Duration.ofMinutes(5)).build();

    private CmdUtils() {
    }

    public static CmdMeta getCmdMeta(int cmd) {

        CmdMeta result = CMD_META_MAP.getIfPresent(cmd);
        if (Objects.nonNull(result)) {
            return result;
        }
        CmdConfigService cmdConfigService = ContextUtil.getBean(CmdConfigService.class);
        CmdConfig config = cmdConfigService.getById(cmd);
        if (Objects.isNull(config)) {
            throw new BusinessException(String.format("cmd %s 未配置", cmd));
        }
        CmdMeta cmdMeta = ParamsUtil.copyProperties(config, CmdMeta.class);
        CMD_META_MAP.put(cmd, cmdMeta);
        return cmdMeta;

    }

    private static byte[] intToBytes(int value) {
        byte[] src = new byte[4];
        src[3] = (byte) ((value >> 24) & 0xFF);
        src[2] = (byte) ((value >> 16) & 0xFF);
        src[1] = (byte) ((value >> 8) & 0xFF);
        src[0] = (byte) (value & 0xFF);
        return src;
    }

    public static int bytesToInt(byte[] src, int offset) {
        int value;
        value = (src[offset] & 0xFF)
                | ((src[offset + 1] & 0xFF) << 8)
                | ((src[offset + 2] & 0xFF) << 16)
                | ((src[offset + 3] & 0xFF) << 24);
        return value;
    }

    private static byte[] byteMerger(byte[] bt1, byte[] bt2) {
        byte[] bt3 = new byte[bt1.length + bt2.length];
        System.arraycopy(bt1, 0, bt3, 0, bt1.length);
        System.arraycopy(bt2, 0, bt3, bt1.length, bt2.length);
        return bt3;
    }

    public static ByteBuf merge(int cmd, byte[] bytes) {
        byte[] cmdBytes = intToBytes(cmd);
        byte[] result = byteMerger(cmdBytes, bytes);
        return Unpooled.wrappedBuffer(result);
    }

    public static ByteBuf merge(int cmd, LiveBaseReq req) {
        byte[] bytes = SerializationUtil.serializeToByte(req);
        return merge(cmd, bytes);
    }

    public static Info split(ByteBuf buf) {

        int cmd = bytesToInt(ByteBufUtil.getBytes(buf, 0, 4), 0);
        byte[] bytes = ByteBufUtil.getBytes(buf, 4, buf.readableBytes() - 4);
        Info info = new Info();
        info.setCmd(cmd);
        info.setData(bytes);
        return info;
    }

    @Data
    public static class Info {
        int cmd;
        byte[] data;
    }


}
